/**
 * 
 */
package gui.text;

import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.concurrent.ExecutionException;

import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.DefaultStyledDocument;
import javax.swing.text.Document;
import javax.swing.text.GapContent;
import javax.swing.text.StyleContext;

import libraries.Library;
import parsetree.Insert;
import parsetree.ParseTree;
import parsetree.Removal;


/**
 * @author Joshua Lockerman
 * 
 */
public class MathDocument extends DefaultStyledDocument {
    
    private final ParseTree parseTree;
    
    private transient PropertyChangeSupport propertyChangeSupport;
    
    public MathDocument(Library lib) throws ExecutionException
    {
        super(new GapContent(4096), new StyleContext());
        parseTree = new ParseTree(this, lib);
        propertyChangeSupport = new PropertyChangeSupport(this);
        //fireChangedUpdate(new DefaultDocumentEvent(0, 0, EventType.CHANGE));
    }
    
    @Override
    public void insertString(int offs, String str, AttributeSet a) throws BadLocationException
    {
        super.insertString(offs, str, a);
    }
    
    @Override
    protected void insertUpdate(DefaultDocumentEvent chng, AttributeSet attr)
    {
        writeLock();
        super.insertUpdate(chng, attr);
        try {
            parseTree.insertUpdate(new FowardingInsert(chng), attr);
        }
        finally {
            writeUnlock();
        }
    }
    
    @Override
    protected void postRemoveUpdate(DefaultDocumentEvent chng)
    {
        writeLock();
        super.postRemoveUpdate(chng);
        try {
            parseTree.postRemoveUpdate(new FowardingRemoval(chng));
        }
        finally {
            writeUnlock();
        }
        fireRemoveUpdate(chng);
    }
    
    public boolean verify()
    {
        return parseTree.verify();
    }
    
    public void setTitle(String saveLocation)
    {
        Object oldName = getProperty(Document.TitleProperty);
        putProperty(Document.TitleProperty, saveLocation);
        propertyChangeSupport.firePropertyChange(Document.TitleProperty, Document.TitleProperty,
                saveLocation);
    }
    
    public String getTitle()
    {
        return (String) getProperty(Document.TitleProperty);
    }
    
    public void addTitleChangeListener(PropertyChangeListener listener)
    {
        propertyChangeSupport.addPropertyChangeListener(Document.TitleProperty, listener);
    }
    
    private void writeObject(ObjectOutputStream out) throws IOException
    {
        out.defaultWriteObject();
    }
    
    private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException
    {
        in.defaultReadObject();
        propertyChangeSupport = new PropertyChangeSupport(this);
    }
    
    private class FowardingInsert implements Insert {
        
        private final DefaultDocumentEvent change;
        
        public FowardingInsert(DefaultDocumentEvent chng)
        {
            change = chng;
        }
        
        @Override
        public int getOffset()
        {
            return change.getOffset();
        }
        
        @Override
        public int getLength()
        {
            return change.getLength();
        }
    }
    
    private class FowardingRemoval implements Removal {
        
        private final DefaultDocumentEvent change;
        
        public FowardingRemoval(DefaultDocumentEvent chng)
        {
            change = chng;
        }
        
        @Override
        public int getOffset()
        {
            return change.getOffset();
        }
        
        @Override
        public int getLength()
        {
            return change.getLength();
        }
        
    }
}
